import { _decorator, Component, Node, Vec2, Vec3, Color, UITransform, PhysicsSystem, EventTouch, Camera, NodeEventType, Sprite, Label, UIOpacity, MeshRenderer, BlockInputEvents, v3, EventMouse, director } from 'cc';
const { ccclass } = _decorator;

/**
 * 通用场景父类，封装常用方法
 * @example
 * export class MyScene extends BaseScene {}
 */
@ccclass('BaseScene')
export class BaseScene extends Component {
    /**
     * 开启物理引擎，并可设置重力、调试绘制等参数
     * @param options 可选参数 { gravity?: Vec3, debugDrawFlags?: number, allowSleep?: boolean }
     * @example
     * this.enablePhysics({ gravity: new Vec3(0, -20, 0), debugDrawFlags: 1 });
     */
    public enablePhysics(options?: { gravity?: Vec3, debugDrawFlags?: number, allowSleep?: boolean }): void {
        const ps = PhysicsSystem.instance;
        ps.enable = true;
        if (options?.gravity) ps.gravity = options.gravity;
        if (options?.debugDrawFlags !== undefined) ps.debugDrawFlags = options.debugDrawFlags;
        if (options?.allowSleep !== undefined) ps.allowSleep = options.allowSleep;
    }

    /**
     * 关闭物理引擎
     * @example
     * this.disablePhysics();
     */
    public disablePhysics(): void {
        PhysicsSystem.instance.enable = false;
    }

    /**
     * 添加2D点击事件
     * @example
     * this.add2DClick(this.node, (event) => { ... });
     */
    public add2DClick(node: Node, callback: (event: EventTouch) => void): void {
        node.off(NodeEventType.TOUCH_END)
        node.scale = v3(1, 1, 1)
        node.on(NodeEventType.TOUCH_START, () => {
            node.scale = v3(0.9, 0.9, 1)
        });
        node.on(NodeEventType.TOUCH_END, (event) => {
            node.scale = v3(1, 1, 1)
            callback(event)
        });
    }

    /**
     * 添加2D拖动事件
     * @example
     * this.add2DDrag(this.node, (delta: Vec2) => { ... });
     */
    public add2DDrag(node: Node, onDrag: (delta: Vec2) => void): void {
        node.on(NodeEventType.TOUCH_MOVE, (event: EventTouch) => {
            const pos = event.getDelta();
            onDrag(pos);
        });
    }

     /**
     * 获取当前场景的主相机（第一个 Camera 组件）
     */
     public static getMainCamera(): Camera | null {
        const scene = director.getScene();
        if (!scene) return null;
        let camera: Camera | null = null;
        scene.walk((node: Node) => {
            const cam = node.getComponent(Camera);
            if (cam && !camera) {
                camera = cam;
            }
        });
        return camera;
    }

    /**
     * 3D 节点点击检测（射线检测，需物理系统开启）
     * @example
     * CommonUtils.add3DClick(event, (node) => { ... });
     * @param event 触摸或鼠标事件
     * @param callback 回调，返回被点击的节点或 null
     * @param camera 可选，指定相机，不传则自动查找主相机
     */
    public static add3DClick(event: EventTouch | EventMouse, callback: (node: Node | null) => void, camera?: Camera) {
        if (!camera) {
            camera = this.getMainCamera();
            if (!camera) {
                callback(null);
                return;
            }
        }
        // 获取屏幕坐标
        let screenPos: Vec2;
        if (event instanceof EventTouch) {
            screenPos = event.getLocation();
        } else if (event instanceof EventMouse) {
            screenPos = new Vec2(event.getLocationX(), event.getLocationY());
        } else {
            callback(null);
            return;
        }
        // 屏幕坐标转世界射线
        const ray = camera.screenPointToRay(screenPos.x, screenPos.y);
        // 物理射线检测
        if (PhysicsSystem.instance.raycast(ray)) {
            const results = PhysicsSystem.instance.raycastResults;
            if (results.length > 0) {
                callback(results[0].collider.node);
                return;
            }
        }
        callback(null);
    }

    /**
     * 获取节点世界坐标
     * @example
     * const pos = this.getWorldPosition(this.node);
     */
    public getWorldPosition(node: Node): Vec3 {
        return node.worldPosition.clone();
    }

    /**
     * 设置节点世界坐标
     * @example
     * this.setWorldPosition(this.node, new Vec3(1,2,3));
     */
    public setWorldPosition(node: Node, pos: Vec3): void {
        node.setWorldPosition(pos);
    }

    /**
     * 获取节点本地坐标
     * @example
     * const pos = this.getPosition(this.node);
     */
    public getPosition(node: Node): Vec3 {
        return node.position.clone();
    }

    /**
     * 设置节点本地坐标
     * @example
     * this.setPosition(this.node, new Vec3(1,2,3));
     */
    public setPosition(node: Node, pos: Vec3): void {
        node.setPosition(pos);
    }

    /**
     * 设置节点层级（zIndex）
     * @example
     * this.setSiblingIndex(this.node, 2);
     */
    public setSiblingIndex(node: Node, index: number): void {
        node.setSiblingIndex(index);
    }

    /**
     * 设置节点透明度（自动适配 Sprite、Label、UIOpacity）
     * @example
     * this.setOpacity(this.node, 128);
     */
    public setOpacity(node: Node, opacity: number): void {
        // 优先 UIOpacity
        const uiOpacity = node.getComponent(UIOpacity);
        if (uiOpacity) {
            uiOpacity.opacity = opacity;
            return;
        }
        // Sprite/Label
        const sprite = node.getComponent(Sprite);
        if (sprite) {
            sprite.color = new Color(sprite.color.r, sprite.color.g, sprite.color.b, opacity);
            return;
        }
        const label = node.getComponent(Label);
        if (label) {
            label.color = new Color(label.color.r, label.color.g, label.color.b, opacity);
            return;
        }
    }

    /**
     * 获取节点透明度（自动适配 Sprite、Label、UIOpacity）
     * @example
     * const opacity = this.getOpacity(this.node);
     */
    public getOpacity(node: Node): number | null {
        const uiOpacity = node.getComponent(UIOpacity);
        if (uiOpacity) return uiOpacity.opacity;
        const sprite = node.getComponent(Sprite);
        if (sprite) return sprite.color.a;
        const label = node.getComponent(Label);
        if (label) return label.color.a;
        return null;
    }

    /**
     * 设置节点颜色（自动适配 Sprite、Label、MeshRenderer）
     * @example
     * this.setColor(this.node, new Color(255,0,0));
     */
    public setColor(node: Node, color: Color): void {
        const sprite = node.getComponent(Sprite);
        if (sprite) {
            sprite.color = color;
            return;
        }
        const label = node.getComponent(Label);
        if (label) {
            label.color = color;
            return;
        }
        const mesh = node.getComponent(MeshRenderer);
        if (mesh && mesh.materials.length > 0) {
            mesh.materials.forEach(mat => {
                if (mat) mat.setProperty('albedo', color);
            });
        }
    }

    /**
     * 获取节点颜色（自动适配 Sprite、Label、MeshRenderer）
     * @example
     * const color = this.getColor(this.node);
     */
    public getColor(node: Node): Color | null {
        const sprite = node.getComponent(Sprite);
        if (sprite) return sprite.color.clone();
        const label = node.getComponent(Label);
        if (label) return label.color.clone();
        const mesh = node.getComponent(MeshRenderer);
        if (mesh && mesh.materials.length > 0) {
            // 取第一个材质的 albedo
            const mat = mesh.materials[0];
            if (mat && mat.getProperty) {
                const c = mat.getProperty('albedo');
                if (c instanceof Color) return c.clone();
            }
        }
        return null;
    }

    /**
     * 递归查找子节点（按名称）
     * @example
     * const child = this.findChildByName(this.node, 'targetName');
     */
    public findChildByName(node: Node, name: string): Node | null {
        for (const child of node.children) {
            if (child.name === name) return child;
            const found = this.findChildByName(child, name);
            if (found) return found;
        }
        return null;
    }

    /**
     * 递归设置节点及其所有子节点的 active 状态
     * @example
     * this.setActiveRecursively(this.node, false);
     */
    public setActiveRecursively(node: Node, active: boolean): void {
        node.active = active;
        node.children.forEach(child => this.setActiveRecursively(child, active));
    }

    /**
     * 判断节点是否在场景树中
     * @example
     * this.isInScene(this.node);
     */
    public isInScene(node: Node): boolean {
        let cur: Node | null = node;
        while (cur) {
            if (cur.parent == null) return false;
            if (cur.parent.name === 'Scene') return true;
            cur = cur.parent;
        }
        return false;
    }

    /**
     * 获取节点尺寸（UITransform）
     * @example
     * const size = this.getNodeSize(this.node);
     */
    public getNodeSize(node: Node): { width: number, height: number } | null {
        const ui = node.getComponent(UITransform);
        if (ui) {
            return { width: ui.width, height: ui.height };
        }
        return null;
    }

    /**
     * 设置节点尺寸（UITransform）
     * @example
     * this.setNodeSize(this.node, 100, 200);
     */
    public setNodeSize(node: Node, width: number, height: number): void {
        const ui = node.getComponent(UITransform);
        if (ui) {
            ui.width = width;
            ui.height = height;
        }
    }

    /**
     * 获取节点旋转角度（欧拉角）
     * @example
     * const euler = this.getEulerAngles(this.node);
     */
    public getEulerAngles(node: Node): Vec3 {
        return node.eulerAngles.clone();
    }

    /**
     * 设置节点旋转角度（欧拉角）
     * @example
     * this.setEulerAngles(this.node, new Vec3(0, 90, 0));
     */
    public setEulerAngles(node: Node, euler: Vec3): void {
        node.eulerAngles = euler;
    }

    /**
     * 获取节点缩放
     * @example
     * const scale = this.getScale(this.node);
     */
    public getScale(node: Node): Vec3 {
        return node.scale.clone();
    }

    /**
     * 设置节点缩放
     * @example
     * this.setScale(this.node, new Vec3(2,2,2));
     */
    public setScale(node: Node, scale: Vec3): void {
        node.setScale(scale);
    }

    /**
     * 关闭当前界面触摸，创建一个可设透明度的屏蔽层（默认全透明）
     * @param parent 父节点，默认 this.node
     * @param opacity 屏蔽层透明度（0-255），默认0全透明
     * @returns 屏蔽层节点
     * @example
     * this.addTouchBlocker(); // 默认全透明
     * this.addTouchBlocker(this.node, 128); // 半透明
     */
    public addTouchBlocker(parent?: Node, opacity: number = 0): Node {
        const p = parent || this.node;
        const blocker = new Node('TouchBlocker');
        blocker.layer = p.layer;
        blocker.setParent(p);
        // 设置全屏尺寸
        const ui = p.getComponent(UITransform);
        if (ui) {
            const blockerUI = blocker.addComponent(UITransform);
            blockerUI.width = ui.width;
            blockerUI.height = ui.height;
        }
        // 添加 BlockInputEvents 组件拦截触摸
        blocker.addComponent(BlockInputEvents);
        // 设置透明度
        const blockerOpacity = blocker.addComponent(UIOpacity);
        blockerOpacity.opacity = opacity;
        return blocker;
    }

    /**
     * 开启全局触摸屏蔽（全局唯一，自动添加到 Canvas 下）
     * @param opacity 屏蔽层透明度（0-255），默认0全透明
     * @returns 屏蔽层节点
     * @example
     * this.showTouchView();
     * this.showTouchView(128);
     */
    public showTouchView(opacity: number = 0): Node | null {
        const canvas = this.getCanvasNode();
        if (!canvas) return null;
        // 检查是否已存在全局屏蔽层
        let blocker = canvas.getChildByName('GlobalTouchBlocker');
        if (!blocker) {
            blocker = new Node('GlobalTouchBlocker');
            blocker.layer = canvas.layer;
            blocker.setParent(canvas);
            // 设置全屏尺寸
            const ui = canvas.getComponent(UITransform);
            if (ui) {
                const blockerUI = blocker.addComponent(UITransform);
                blockerUI.width = ui.width;
                blockerUI.height = ui.height;
            }
            blocker.addComponent(BlockInputEvents);
            const blockerOpacity = blocker.addComponent(UIOpacity);
            blockerOpacity.opacity = opacity;
        } else {
            const blockerOpacity = blocker.getComponent(UIOpacity);
            if (blockerOpacity) blockerOpacity.opacity = opacity;
            blocker.active = true;
        }
        return blocker;
    }

    /**
     * 关闭全局触摸屏蔽（隐藏全局唯一屏蔽层）
     * @example
     * this.hideTouchView();
     */
    public hideTouchView(): void {
        const canvas = this.getCanvasNode();
        if (!canvas) return;
        const blocker = canvas.getChildByName('GlobalTouchBlocker');
        if (blocker) blocker.active = false;
    }

    /**
     * 获取 Canvas 根节点
     * @returns Canvas 节点或 null
     */
    private getCanvasNode(): Node | null {
        let cur: Node | null = this.node;
        while (cur) {
            if (cur.name === 'Canvas') return cur;
            cur = cur.parent;
        }
        return null;
    }

    // 拖拽信息缓存
    private _dragInfoMap: Map<Node, {
        pos: Vec3,
        parent: Node,
        siblingIndex: number
    }> = new Map();

    /**
     * 节点拖拽（支持拖拽到专用层，松手后回到原父级和层级，可监听拖拽事件）
     * 拖拽信息自动记录，可用 resetNodeDrag(node) 回到原位
     * @param node 要拖拽的节点
     * @param dragLayer 拖拽层节点（如全局 DragLayer），不传则直接在原父级拖拽
     * @param callbacks 拖拽事件回调 { onStart, onMove, onEnd, onCancel }
     * @example
     * this.enableNodeDrag(node, dragLayer, { ... });
     */
    public enableNodeDrag(
        node: Node,
        dragLayer?: Node,
        callbacks?: {
            onStart?: () => void,
            onMove?: (pos: Vec2) => void,
            onEnd?: () => void,
            onCancel?: () => void
        }
    ): void {
        let dragging = false;
        let dragOffset: Vec2 = new Vec2();

        node.on(NodeEventType.TOUCH_START, (event: EventTouch) => {
            dragging = true;
            // 记录原始信息
            this._dragInfoMap.set(node, {
                pos: node.position.clone(),
                parent: node.parent!,
                siblingIndex: node.getSiblingIndex()
            });
            // 拖拽到专用层
            if (dragLayer && node.parent !== dragLayer) {
                node.setParent(dragLayer);
            }
            // 计算偏移
            const ui = node.getComponent(UITransform);
            if (ui) {
                const touchPos = event.getUILocation();
                const nodePos = ui.convertToNodeSpaceAR(new Vec3(touchPos.x, touchPos.y, 0));
                dragOffset.set(nodePos.x, nodePos.y);
            }
            if (callbacks?.onStart) callbacks.onStart();
        });

        node.on(NodeEventType.TOUCH_MOVE, (event: EventTouch) => {
            if (!dragging) return;
            const ui = node.getComponent(UITransform);
            if (ui) {
                const touchPos = event.getUILocation();
                const nodePos = ui.convertToNodeSpaceAR(new Vec3(touchPos.x, touchPos.y, 0));
                node.setPosition(new Vec3(nodePos.x - dragOffset.x, nodePos.y - dragOffset.y, node.position.z));
                if (callbacks?.onMove) callbacks.onMove(new Vec2(node.position.x, node.position.y));
            }
        });

        node.on(NodeEventType.TOUCH_END, () => {
            if (!dragging) return;
            dragging = false;
            // this.resetNodeDrag(node);

            if (callbacks?.onEnd) callbacks.onEnd();
            if (callbacks?.onEnd) {
                callbacks.onEnd();
            }
            if (this._dragInfoMap.has(node)) {
                this._dragInfoMap.delete(node);
            }

        });

        node.on(NodeEventType.TOUCH_CANCEL, () => {
            if (!dragging) return;
            dragging = false;
            // this.resetNodeDrag(node);
            if (callbacks?.onCancel) {
                callbacks.onCancel();
            }
            if (this._dragInfoMap.has(node)) {
                this._dragInfoMap.delete(node);
            }

        });
    }

    /**
     * 让节点回到拖拽前的位置（自动使用记录信息）
     * @param node 拖拽节点
     * @example
     * this.resetNodeDrag(node);
     */
    public resetNodeDrag(node: Node): void {
        const info = this._dragInfoMap.get(node);
        if (info) {
            node.setParent(info.parent);
            node.setSiblingIndex(info.siblingIndex);
            node.setPosition(info.pos);
            this._dragInfoMap.delete(node);
        }
    }

    /**
     * 关闭节点拖拽（移除事件监听，清除拖拽信息）
     * @param node 拖拽节点
     * @example
     * this.disableNodeDrag(node);
     */
    public disableNodeDrag(node: Node): void {
        node.off(NodeEventType.TOUCH_START);
        node.off(NodeEventType.TOUCH_MOVE);
        node.off(NodeEventType.TOUCH_END);
        node.off(NodeEventType.TOUCH_CANCEL);
        this._dragInfoMap.delete(node);
    }
} 